package com.study.chunguard.db.config;

import cn.hutool.core.text.CharPool;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.study.chunguard.common.constant.DataStatus;
import com.study.chunguard.db.properties.MybatisPlusAutoFillProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Optional;

/**
 * 自定义填充公共字段
 *
 * @author zlt
 * @since 2018/12/11
 * <p>
 * Blog: <a href="https://zlt2000.gitee.io">...</a>
 * Github: <a href="https://github.com/zlt2000">...</a>
 */
@Slf4j
public class DefaultMetaObjectHandler implements MetaObjectHandler {
    private final MybatisPlusAutoFillProperties autoFillProperties;

    public DefaultMetaObjectHandler(MybatisPlusAutoFillProperties autoFillProperties) {
        this.autoFillProperties = autoFillProperties;
    }

    /**
     * 是否开启了插入填充
     */
    @Override
    public boolean openInsertFill() {
        return autoFillProperties.getEnableInsertFill();
    }

    /**
     * 是否开启了更新填充
     */
    @Override
    public boolean openUpdateFill() {
        return autoFillProperties.getEnableUpdateFill();
    }

    /**
     * 插入填充，字段为空自动填充
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        if (log.isDebugEnabled()) {
            log.debug("开始执行插入的自动填充 ....:{}", metaObject);
        }
        Date date = new Date();
        // 创建时间
        fillFields(metaObject, autoFillProperties.getCreateTimeField(), date);
        // 更新时间
        fillFields(metaObject, autoFillProperties.getUpdateTimeField(), date);
        // 逻辑删除
        fillFields(metaObject, autoFillProperties.getDeletedField(), DataStatus.VALID);
    }

    /**
     * 更新填充
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        if (log.isDebugEnabled()) {
            log.debug("开始执行更新的自动填充 ....:{}", metaObject);
        }
        Date date = new Date();
        // 更新时间
        fillFields(metaObject, autoFillProperties.getUpdateTimeField(), date);
    }



    private void fillFields(MetaObject metaObject, String fieldNames, Object value) {
        if (CharSequenceUtil.isNotBlank(fieldNames)) {
            String[] fields = fieldNames.split(String.valueOf(CharPool.COMMA));
            for (String field : fields) {
                Object convertedValue = convertValueIfNeeded(metaObject, field.trim(), value);
                fillStrategy(metaObject, field.trim(), convertedValue);
            }
        }
    }

    /**
     * 由于修改用户ID的数值类型，有时候是Long，有时候是Integer，以及创建和修改时间的时间类型，有时候是Date，有时候是LocalDateTime，所以需要转换一下
     *
     * @param metaObject metaObject
     * @param fieldName  fieldName
     * @param value      value
     * @return Object
     */
    private Object convertValueIfNeeded(MetaObject metaObject, String fieldName, Object value) {
        if (!metaObject.hasSetter(fieldName) || value == null) {
            return null;
        }

        Class<?> fieldType = metaObject.getSetterType(fieldName);

        if (value instanceof Number) {
            return handleNumberType(fieldType, (Number) value);
        } else if (value instanceof Date) {
            return handleDateType(fieldType, (Date) value);
        } else if (value instanceof LocalDateTime) {
            return handleLocalDateTimeType(fieldType, (LocalDateTime) value);
        }

        return value;
    }

    private Object handleNumberType(Class<?> fieldType, Number numberValue) {
        if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
            return numberValue.intValue();
        } else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
            return numberValue.longValue();
        } else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
            return numberValue.floatValue();
        } else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
            return numberValue.doubleValue();
        } else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
            return numberValue.byteValue();
        } else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
            return numberValue.shortValue();
        } else if (fieldType.equals(BigDecimal.class)) {
            return new BigDecimal(numberValue.toString());
        } else if (fieldType.equals(BigInteger.class)) {
            return BigInteger.valueOf(numberValue.longValue());
        }
        return numberValue;
    }

    private Object handleDateType(Class<?> fieldType, Date dateValue) {
        if (fieldType.equals(LocalDateTime.class)) {
            return dateValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        } else if (fieldType.equals(LocalDate.class)) {
            return dateValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        }
        return dateValue;
    }

    private Object handleLocalDateTimeType(Class<?> fieldType, LocalDateTime localDateTimeValue) {
        if (fieldType.equals(Date.class)) {
            return Date.from(localDateTimeValue.atZone(ZoneId.systemDefault()).toInstant());
        }
        return localDateTimeValue;
    }
}